﻿/********************************************************************************
** @ Date:2025/04/30
** @ Author: Gaojia
** @ Description: ModBus通讯类
** @ Version:1.0.0
** @ Fun:
********************************************************************************/
#ifndef CModBusOper_H
#define CModBusOper_H


#include <qobject.h>
#include <QVector>
#include <QMap>
#include <QModbusTcpClient>
#include <QMutex>
#include <QWaitCondition>
#include <QThread>
enum  EModEvent {
	enNone = 0,
	enReadAndWrite = 1,
	enReadWait = 2,
	enWriteWait = 3
};

struct SModData
{
	int nAddr;               //起始地址
	int nLength;             //长度
	int nDataType;           //数据类型 线圈  寄存器 报错寄存器
	int nSlaveId;            //从站ID
	QVector<int>vecReadVal;  //读取到的数据
	QVector<int>vecWriteVal; //写入的数据
	int  nRetryCount;        // 重试次数
	bool bExecSuc;         //是否执行成功      
	SModData()
	{
		nAddr = 40001;
		nLength = 0;
		nDataType = 3;
		nSlaveId = 1;
		nRetryCount = 0;
		bExecSuc = false;
	}
	void resetData()
	{
		nRetryCount = 0;
		bExecSuc = false;
		vecReadVal.clear();
	}
};
class CModBusOper :
	public QObject
{
	Q_OBJECT
public:
	CModBusOper(QString sIp, int nPort,int nType=3);                        //数据类型 默认为3  
	~CModBusOper();
	void threadStop();                                          //停止线程
	void setSlaveId(int nId);                                   //设置从站号
	bool ReadVal(int nAddr, int &);                             //读单个地址寄存器值
	bool ReadVal(int nAddr, int nLength, int &);                //读多个寄存器地址拼成一个值
	bool ReadValEx(int nAddr, int nLength, QVector<int>&);      //读多个机器人地址值
	bool ReadValExLoop(int nAddr, int nLength, QVector<int>&);   //读多个机器人地址值  阻塞
	bool WriteVal(int nAddr, int nVal);                         //写一个地址值
	bool WriteValCoil(int nAddr, int);                          //写一个线圈地址值
	bool WriteVal(int nAddr, int nLength, int);                 //一个值分成多个寄存器地址写值 
	bool WriteValEx(int nAddr, int nLength, QVector<int>);      //写多个地址值
	QString GerReError();                                       //获取失败返回字符串
	bool m_bModConnect;                                         //Modbus 是否连接成功   
private:
	void initMod();                                             //初始化modbus
	bool ReadValLoop(SModData& ModData);                        //阻塞读取数据
	bool readValsOper();                                        //给上层调用读数据  
	bool writeValsOper();                                       //给上层调用写数据
	bool threadReadVal(SModData &ModData);                      //modbus通讯读数据 
	bool threadWriteVal(SModData &ModData);                     //modbus通讯写数据     
	void sleepSysMs(int nMs);                                   //线程休眠
	bool waitSys(int nSystime);                                 //线程同步阻塞nSystime毫秒
	void execRead();                                            //读事件
	void execWrite();                                           //写事件
	void execReadWait();                                        //阻塞读事件
	void execWriteWait();                                       //阻塞写事件
	void setModDataWait(SModData cModData,int nType=0);         //修改m_cModDataR或者m_cModDataW
	SModData getModDataWait(int nType = 0);                     //获取m_cModDataR或者m_cModDataW   
	void addModData(SModData cModData, int nType = 0);
	void setModData(int nIndex, SModData cModData, int nType = 0);
	void removeModDataW(int nIndex);
	void removeModDataR(int nIndex);
	SModData getModData(int nIndex, int nType = 0);
	void setModDataMap(SModData cModData);
	SModData getModDataMap(int nAddr);
	int getModDataSize(int nType = 0);      //获取读写数组大小
private slots:
	void slot_Work();
private:
	EModEvent  m_enThreadCmd;                                 //线程工作命令    0 无任务 1无阻塞读写  2 阻塞读数据  3阻塞写数据
	bool m_bThread;                                           //异步线程状态
	QMap<int, SModData>m_mapModDataR;                         //modbus读缓存数据
	QMutex *m_pMutexDate;                                     //读数据缓存锁  
	QVector<SModData>m_vecModDataR;                           //线程实时读的数据 读完成更新到m_mapModData;
	QVector<SModData>m_vecModDataW;                           //线程实时写的数据 写完删除
	QMutex *m_pLockRw;                                        //读写数组操作锁 
	SModData m_cModDataR;                                     //线程阻塞读的数据
	SModData m_cModDataW;                                     //线程阻塞写的数据
	QMutex *m_pLockRwWait;                                    //阻塞读写对象操作锁 
	int m_nReadIndex;                                         //当前读数据的序号
	QModbusTcpClient *m_pModTcp;                              //ModbusTCP客户端对象
	QMutex *m_pSysMutex;                                      //线程同步锁
	QMutex *m_pLock;                                          //资源锁 每次只允许一个线程拿到该锁
	QWaitCondition *m_pWaitCondition;                         //线程同步信号
	QThread *m_pThread;
	QString m_sIp;
	int m_nPort;
};

#endif